use std::{
    sync::Arc,
    time::{Duration, Instant},
};

use crate::{
    error::TonglunError, globals::POOL_RUNTIME, pools::PoolEntry, utils::NullStr, values::ValueRef,
};

const DEBUG_LOCK_TIME_WARN: Duration = Duration::from_millis(1500);

#[derive(Debug, Clone)]
pub struct TonglunFunction {
    pub fptr: unsafe extern "C" fn(data: u64, out: *mut ValueRef, args: *const ValueRef, nargs: u64) -> u64,
    pub data: u64,
    pub runtime_id: u32,
    pub name: Arc<NullStr>,
    pub thread_safe: bool,
}

impl TonglunFunction {
    pub unsafe fn do_call(self, out: *mut ValueRef, args: *const ValueRef, nargs: u64) -> u64 {
        if self.thread_safe {
            // directly call
            return unsafe { (self.fptr)(self.data, out, args, nargs) };
        }

        // use runtime lock
        let Some(runtime_lock) = POOL_RUNTIME
            .read()
            .get(PoolEntry::new(self.runtime_id as usize))
            .map(|it| it.lock.clone())
        else {
            return TonglunError::RuntimeNotFound(self.runtime_id as usize).into_usize() as u64;
        };

        loop {
            match runtime_lock.try_lock_until(Instant::now() + DEBUG_LOCK_TIME_WARN) {
                Some(_guard) => {
                    return unsafe { (self.fptr)(self.data, out, args, nargs) };
                }
                None => {
                    println!("Calling function {} has taken too long, which is suspicious to be a deadlock", self.name.as_str());
                    continue;
                }
            }
        }
    }
}
